package cn.funeralobjects.project.aliyun.ddns.utils;

import cn.funeralobjects.project.aliyun.ddns.model.DomainRecord;
import cn.funeralobjects.util.CsvUtils;
import cn.funeralobjects.utils.aliyun.core.AliyunClientFactory;
import cn.funeralobjects.utils.aliyun.dns.DnsAction;
import cn.funeralobjects.utils.aliyun.dns.enums.DescribeType;
import cn.funeralobjects.utils.aliyun.dns.model.AddDomainRecordParam;
import cn.funeralobjects.utils.aliyun.dns.model.DescribeSubDomainRecordsData;
import cn.funeralobjects.utils.aliyun.dns.model.DescribeSubDomainRecordsParam;
import cn.funeralobjects.utils.aliyun.dns.model.UpdateDomainRecordParam;
import com.aliyuncs.exceptions.ClientException;
import org.slf4j.Logger;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author FuneralObjects
 * Create date: 2020/11/29 9:56 PM
 */
public class DdnsUtils {

    private static final String ENABLE = "ENABLE";

    public static List<DomainRecord> loadRecords(String fileLocation, String ignorePrefix, Logger logger) {
        logger.info("load file: {}", fileLocation);
        File file = new File(fileLocation);
        if (!file.exists() || !file.isFile()) {
            return Collections.emptyList();
        }

        return CsvUtils.readFile(file)
                .stream()
                .filter(row -> row.length >= 4 && !row[0].startsWith(ignorePrefix))
                .map(row -> {
                    logger.debug("loaded record: {}", Arrays.toString(row));
                    return new DomainRecord()
                            .setSubDomain(row[0].trim())
                            .setDomain(row[1].trim())
                            .setAccessKeyId(row[2].trim())
                            .setAccessKeySecret(row[3].trim());
                }).collect(Collectors.toList());
    }


    public static DescribeSubDomainRecordsData loadDescribeRecords(DomainRecord domainRecord, Logger logger) {
        AliyunClientFactory factory = AliyunClientFactory.builder().setAccessKeyId(domainRecord.getAccessKeyId())
                .setAccessKeySecret(domainRecord.getAccessKeySecret())
                .build();
        try {
            return factory.buildClient(DnsAction.DESCRIBE_SUB_DOMAIN_RECORDS)
                    .doAction(new DescribeSubDomainRecordsParam()
                            .setType(DescribeType.A)
                            .setSubDomain(domainRecord.getSubDomain() + "." + domainRecord.getDomain())
                            .setDomainName(domainRecord.getDomain())
                    ).getData();
        } catch (ClientException e) {
            logger.error("Loaded records from Aliyun failed", e);
            return null;
        }
    }

    public static boolean isCanBeUpdate(String lastIp, DescribeSubDomainRecordsData recordsData, Logger logger) {
        return recordsData.getTotalCount() <= 0 || recordsData.getDomainRecords().getRecords()
                .stream()
                .anyMatch(record -> {
                    boolean isEnable = ENABLE.equals(record.getStatus());
                    if (!isEnable) {
                        logger.error("This record is disabled, domain: {}.{}, status: {}", record.getRr(), record.getDomainName(), record.getStatus());
                        return false;
                    }
                    boolean isLocked = record.getLocked();
                    if (isLocked) {
                        logger.error("This record is locked. domain: {}.{}, status: {}", record.getRr(), record.getDomainName(), record.getStatus());
                        return false;
                    }

                    boolean isSame = lastIp.equals(record.getValue());
                    if (isSame) {
                        logger.info("This record not needs to updated, skipped. domain: {}.{}", record.getRr(), record.getDomainName());
                        return false;
                    }
                    return true;
                });
    }

    public static String addDescribeRecord(String ip, DomainRecord record, Logger logger) {
        AliyunClientFactory factory = AliyunClientFactory.builder()
                .setAccessKeyId(record.getAccessKeyId())
                .setAccessKeySecret(record.getAccessKeySecret())
                .build();
        try {
            String recordId = factory.buildClient(DnsAction.ADD_DOMAIN_RECORD)
                    .doAction(new AddDomainRecordParam()
                            .setRr(record.getSubDomain())
                            .setType(DescribeType.A)
                            .setDomainName(record.getDomain())
                            .setValue(ip)
                    ).getData().getRecordId();
            logger.info("Added DNS record success! record_id:{} , {}.{} -> {}", recordId, record.getSubDomain(), record.getDomain(), ip);
            return recordId;
        } catch (ClientException e) {
            logger.error("Added DNS record failed.", e);
            return null;
        }
    }

    public static void updateDescribeRecord(String ip, DomainRecord record, DescribeSubDomainRecordsData recordsData, Logger logger) {
        AliyunClientFactory factory = AliyunClientFactory.builder()
                .setAccessKeyId(record.getAccessKeyId())
                .setAccessKeySecret(record.getAccessKeySecret())
                .build();
        recordsData.getDomainRecords()
                .getRecords().stream()
                .findFirst().ifPresent(recordData -> {
            try {
                String recordId = factory.buildClient(DnsAction.UPDATE_DOMAIN_RECORD)
                        .doAction(new UpdateDomainRecordParam()
                                .setRecordId(recordData.getRecordId())
                                .setRr(recordData.getRr())
                                .setLine(recordData.getLine())
                                .setPriority(recordData.getPriority())
                                .setTtl(recordData.getTtl())
                                .setType(recordData.getType())
                                .setValue(ip)
                        ).getData().getRecordId();
                logger.info("Updated DNS record success! record_id:{} , {}.{} -> {}", recordId, recordData.getRr(), recordData.getDomainName(), ip);
            } catch (ClientException e) {
                logger.error("Updated DNS record failed.", e);
            }
        });
    }
}
